using System;
using System.Reflection;
using System.Collections;
using Widgetsphere.Core.Logging;

namespace Widgetsphere.Core.Util
{
	public partial class ReflectionHelper
	{
		#region Constructor

		private ReflectionHelper()
		{
		}

		#endregion

		#region CreateInstance

		public static Object CreateInstance(string assemblyName, string type)
		{
			try
			{
				System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(assemblyName);

				// Calls through reflection mask exceptions in a 
				// TargetInvocationException, which is annoying.
				// Un-mask by rethrowing the inner exception
				object o = assembly.CreateInstance(type, true);
				return o;
			}
			catch (TargetInvocationException e)
			{
				throw e.InnerException;
			}
			catch (Exception ex)
			{
				throw ex;
			}

		}

		public static Object CreateInstance(System.Type type, Object[] args)
		{
			try
			{
				// Calls through reflection mask exceptions in a 
				// TargetInvocationException, which is annoying.
				// Un-mask by rethrowing the inner exception
				return Activator.CreateInstance(type, args);
			}
			catch (TargetInvocationException e)
			{
				throw e.InnerException;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static Object CreateInstance(Type type)
		{
			return Activator.CreateInstance(type);
		}

		#endregion

		#region GetType

		public static Type GetType(string assemblyName, string type)
		{
			try
			{
				System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(assemblyName);

				// Calls through reflection mask exceptions in a 
				// TargetInvocationException, which is annoying.
				// Un-mask by rethrowing the inner exception
				Type o = assembly.GetType(type, true);
				return o;
			}
			catch (TargetInvocationException e)
			{
				throw e.InnerException;
			}
			catch (Exception ex)
			{
				throw ex;
			}

		}

		#endregion

		#region LoadAllAssembliesForPath

		private static ArrayList LoadAllAssembliesForPath(string path)
		{
			//Store the current assembly file name
			string current = Assembly.GetExecutingAssembly().Location.ToLower();

			ArrayList assemblyList = new ArrayList();
			string[] files = System.IO.Directory.GetFiles(path, "*.dll");
			foreach (string fileName in files)
			{
				if (!fileName.ToLower().Equals(current))
				{
					//try loading this file as an assembly
					try
					{
						System.Reflection.Assembly assembly = Assembly.LoadFrom(fileName);
						assemblyList.Add(assembly);
					}
					catch (Exception ex)
					{
						//Do nothing as the file is not a valid VS.NET file
						MessageLog.LogError(LogClass.WSCommonUtil, ex);
					}
				}
			}

			string executingAssemblyPath = Assembly.GetExecutingAssembly().Location;
			System.IO.FileInfo fi = new System.IO.FileInfo(executingAssemblyPath);
			executingAssemblyPath = fi.DirectoryName;
			if (StringHelper.Match(path, executingAssemblyPath, true))
			{
				assemblyList.Add(Assembly.GetExecutingAssembly());
			}
			return assemblyList;
		}

		#endregion

		#region GetObjectsFromPath
		public static System.Type[] GetCreatableObjects(System.Type parentType, string path)
		{
			ArrayList retval = new ArrayList();
			ArrayList al = LoadAllAssembliesForPath(path);

			try
			{
				foreach (System.Reflection.Assembly assembly in al)
				{
					System.Diagnostics.Debug.WriteLine(assembly.Location);
					foreach (Type t in assembly.GetTypes())
					{
						if (parentType.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface)
							retval.Add(t);
						else if (t.IsAssignableFrom(parentType) && !t.IsAbstract && !t.IsInterface)
							retval.Add(t);
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}

			return (System.Type[])retval.ToArray(typeof(System.Type));
		}

		#endregion

		#region GetByAttribute

		public static Hashtable GetFieldsByAttribute(System.Type objectType, System.Type attributeType)
		{
			try
			{
				Hashtable retval = new Hashtable();
				FieldInfo[] fields = objectType.GetFields();
				foreach (FieldInfo field in fields)
				{
					object[] attributes = field.GetCustomAttributes(attributeType, true);
					if (attributes.Length > 0)
					{
						retval.Add(field, attributes[0]);
					}
				}
				return retval;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static Hashtable GetFieldsByAttribute(object o, System.Type attributeType)
		{
			try
			{
				Type objectType = o.GetType();
				Hashtable retval = new Hashtable();
				FieldInfo[] fields = objectType.GetFields();
				foreach (FieldInfo field in fields)
				{
					object[] attributes = field.GetCustomAttributes(attributeType, true);
					if (attributes.Length > 0)
					{
						retval.Add(field, attributes[0]);
					}
				}
				return retval;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static Hashtable GetPropertiesByAttribute(System.Type objectType, System.Type attributeType)
		{
			try
			{
				Hashtable retval = new Hashtable();
				PropertyInfo[] properties = objectType.GetProperties();
				foreach (PropertyInfo property in properties)
				{
					object[] attributes = property.GetCustomAttributes(attributeType, true);
					if (attributes.Length > 0)
					{
						retval.Add(property, attributes[0]);
					}
				}
				return retval;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static Hashtable GetPropertiesByAttribute(object o, System.Type attributeType)
		{
			return GetPropertiesByAttribute(o.GetType(), attributeType);
		}

		public static Hashtable GetMethodsByAttribute(System.Type parentType, System.Type attributeType)
		{
			try
			{
				Hashtable retval = new Hashtable();
				MethodInfo[] methods = parentType.GetMethods();
				foreach (MethodInfo m in methods)
				{
					object[] attributes = m.GetCustomAttributes(attributeType, true);
					if (attributes.Length > 0)
					{
						retval.Add(attributes[0], m);
					}
				}

				//Sort the array

				return retval;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		#endregion

		#region GetSingleAttribute

		public static Attribute GetSingleAttribute(System.Type attributeType, object instance)
		{
			try
			{
				Hashtable retval = new Hashtable();
				Attribute[] attributes = (Attribute[])instance.GetType().GetCustomAttributes(attributeType, true);
				if (attributes.Length > 0)
					return attributes[0];
				else
					return null;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static Attribute GetSingleAttribute(System.Type attributeType, System.Type type)
		{
			try
			{
				Hashtable retval = new Hashtable();
				Attribute[] attributes = (Attribute[])type.GetCustomAttributes(attributeType, true);
				if (attributes.Length > 0)
					return attributes[0];
				else
					return null;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static Attribute[] GetAttributes(System.Type attributeType, object instance)
		{
			try
			{
				Hashtable retval = new Hashtable();
				Attribute[] attributes = (Attribute[])instance.GetType().GetCustomAttributes(attributeType, true);
				return attributes;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		#endregion

		#region IsTypeOf

		public static bool IsTypeOf(System.Type checkType, string baseType)
		{
			if (checkType == null)
				return false;
			while ((checkType != null) && (checkType.ToString() != baseType))
			{
				checkType = checkType.BaseType;
			}
			return (checkType != null);
		}

		public static bool IsTypeOf(System.Type checkType, System.Type baseType)
		{
			return IsTypeOf(checkType, baseType.ToString());
		}

		#endregion

		#region DisplayProperties

		public static void DisplayProperties(object o)
		{
			foreach (PropertyInfo info in o.GetType().GetProperties())
			{
				System.Diagnostics.Debug.WriteLine(info.Name);
			}
		}

		public static void DisplayObjectTypes(object o)
		{
			foreach (System.Type t1 in o.GetType().GetNestedTypes())
			{
				System.Diagnostics.Debug.WriteLine(t1.ToString());
			}
			foreach (System.Type t in o.GetType().GetInterfaces())
			{
				System.Diagnostics.Debug.WriteLine(t.ToString());
			}
			foreach (MethodInfo mi in o.GetType().GetMethods())
			{
				System.Diagnostics.Debug.WriteLine(mi.Name);
			}
		}

		#endregion

		#region Implements Interface
		public static bool ImplementsInterface(object o, Type interfaceType)
		{
			foreach (System.Type t in o.GetType().GetInterfaces())
			{
				if (t == interfaceType)
					return true;
			}
			return false;
		}
		public static bool ImplementsInterface(Type objectType, Type interfaceType)
		{
			foreach (System.Type t in objectType.GetInterfaces())
			{
				if (t == interfaceType)
					return true;
				if (t.Name.Contains("`"))
				{
					if (t.Name == interfaceType.Name && t.Assembly == interfaceType.Assembly)
					{
						return true;
					}
				}
			}
			return false;
		}

		public static System.Type[] GetCreatableObjectImplementsInterface(Type interfaceType, Assembly assembly)
		{
			ArrayList retval = new ArrayList();
			try
			{
				foreach (Type t in assembly.GetTypes())
				{
					if (ImplementsInterface(t, interfaceType) && !t.IsAbstract && !t.IsInterface)
						retval.Add(t);
				}
			}
			catch (Exception ex)
			{
				MessageLog.LogError(LogClass.WSCommonUtil, ex, "Could not load types for assembly: " + assembly.FullName);
			}
			return (System.Type[])retval.ToArray(typeof(System.Type));
		}

		public static System.Type[] GetCreatableObjectImplementsInterface(Type interfaceType, string path)
		{
			ArrayList al = new ArrayList();
			if (System.IO.File.Exists(path))
			{
				try
				{
					al.Add(Assembly.LoadFile(path));
				}
				catch (System.BadImageFormatException ex)
				{
					//Do Nothing
				}
				catch (Exception ex)
				{
					throw;
				}
			}
			else
			{
				al = LoadAllAssembliesForPath(StringHelper.EnsureDirectorySeperatorAtEnd(path));
			}

			ArrayList retval = new ArrayList();
			try
			{
				foreach (System.Reflection.Assembly assembly in al)
				{
					try
					{
						foreach (Type t in assembly.GetTypes())
						{
							if (ImplementsInterface(t, interfaceType) && !t.IsAbstract && !t.IsInterface)
								retval.Add(t);
						}
					}
					catch (System.Reflection.ReflectionTypeLoadException ex)
					{
						MessageLog.LogError(LogClass.WSCommonUtil, ex, "Could not load types for assembly: " + assembly.FullName);
						foreach (Exception innerEx in ex.LoaderExceptions)
						{
							MessageLog.LogError(LogClass.WSCommonUtil, ex, "Loader Exception: " + innerEx.Message);
						}
					}
					catch (Exception ex)
					{
						MessageLog.LogError(LogClass.WSCommonUtil, ex, "Could not load types for assembly: " + assembly.FullName);
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}

			return (System.Type[])retval.ToArray(typeof(System.Type));
		}
		#endregion

	}
}